TCP和UDP详解(非常详细)

您所在的位置:网站首页 udp 可靠传输 TCP和UDP详解(非常详细)

TCP和UDP详解(非常详细)

2023-08-15 03:53| 来源: 网络整理| 查看: 265

TCP和UDP详解

计算机网络知识扫盲:https://blog.csdn.net/hansionz/article/details/85224786 网络编程套接字:https://blog.csdn.net/hansionz/article/details/85226345 HTTP协议详解:https://blog.csdn.net/hansionz/article/details/86137260

前言:本篇博客介绍TCP协议和UDP协议的各个知识点,这两个协议都是位于传输层的协议,我们首先从传输层谈起。

传输层: 传输层是TCP/IP协议五层模型中的第四层。它提供了应用程序间的通信,它负责数据能够从发送端传输到接收端。其功能包括:一、格式化信息流;二、提供可靠传输。为实现后者,传输层协议规定接收端必须发回确认,并且假如分组丢失,必须重新发送。

再谈端口号: 在网络知识扫盲博客中谈到端口号标识了一个主机上进行通信的不同应用程序。在TCP/IP协议中, 用"源IP", "源端口号", "目的IP", "目的端口号", "协议号" 这样一个五元组来标识一个通信(可以通过 netstat -n查看,协议号指的是那个使用协议)。 一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定。

端口号范围划分:

0 - 1023: 知名端口号,HTTP、FTP、 SSH等这些广为使用的应用层协议他们的端口号都是固定的,自己写的程序中,不能随意绑定知名端口号。1024 - 65535:操作系统动态分配的端口号。 客户端程序的端口号,就是由操作系统从这个范围分配的。

常见的知名端口号:

ssh服务器:22端口ftp服务器:21端口http服务器:80端口telnet服务器:23端口https服务器:443端口MYSQL服务器:3306端口

在Linux操作系统中使用命令cat /etc/services可以看到所有的知名端口。

netstat工具: 用来查看网络状态。

n 拒绝显示别名,能显示数字的全部转化成数字l 仅列出有在Listen (监听)的服务状态p 显示正在使用Socket的程序识别码和程序名称t (tcp)仅显示tcp相关选项u u (udp)仅显示udp相关选项a (all)显示所有选项,默认不显示LISTEN相关

pidof [进程名]: 可以根据进程名直接查看服务器的进程id。例如:pidof sshd。

UDP协议

UDP协议报文格式: 在这里插入图片描述

16位UDP长度表示整个数据报(UDP首部+UDP数据)的长度如果校验和出错,就会直接丢弃(UDP校验首部和数据部分)

UDP协议的特点:

无连接:只知道对端的IP和端口号就可以发送,不需要实现建立连接。不可靠:没有确认机制, 没有重传机制。如果因为网络故障该段无法发到对方, UDP协议层也不会给应用层返回任何错误信息。面向数据报: 应用层交给UDP多长的报文, UDP原样发送既不会拆分,也不会合并。如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个 字节,而不能循环调用10次recvfrom, 每次接收10个字节。所以UDP不能够灵活的控制读写数据的次数和数量。

UDP的缓冲区:UDP存在接收缓冲区,但不存在发送缓冲区。

UDP没有发送缓冲区,在调用sendto时会直接将数据交给内核,由内核将数据传给网络层协议进行后续的传输动作。为什么UDP不需要发送缓冲区? 因为UDP不保证可靠性,它没有重传机制,当报文丢失时,UDP不需要重新发送,而TCP不同,他必须具备发送缓冲区,当报文丢失时,TCP必须保证重新发送,用户不会管,所以必须要具备发送缓冲区。

UDP具有接收缓冲区,但是这个接收缓冲区不能保证收到的UDP报文的顺序和发送UDP报的顺序一致,如果缓冲区满了再到达的UDP数据报就会被丢弃。

UDP接收缓冲区和丢包问题:https://blog.csdn.net/ljh0302/article/details/49738191

UDP是一种全双工通信协议。 UDP协议首部中有一个16位的大长度. 也就是说一个UDP能传输的报文长度是64K(包含UDP首部)。如果我们需要传输的数据超过64K, 就需要在应用层手动的分包, 多次发送, 并在接收端手动拼装。

常见的基于UDP的应用层协议:

NFS:网络文件系统TFTP:简单文件传输协议DHCP:动态主机配置协议BOOTP:启动协议(用于无盘设备启动)DNS:域名解析协议程序员在写UDP程序时自己定义的协议 TCP协议

TCP全称传输控制协议,必须对数据的传输进行控制。

TCP协议报文格式: 在这里插入图片描述

源端口号/目的端口号:表示数据从哪个进程来,要到那个进程去

32位序号:序号是可靠传输的关键因素。TCP将要传输的每个字节都进行了编号,序号是本报文段发送的数据组的第一个字节的编号,序号可以保证传输信息的有效性。比如:一个报文段的序号为300,此报文段数据部分共有100字节,则下一个报文段的序号为401。

32位确认序号:每一个ACK对应这一个确认号,它指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。

4位首部长度(数据偏移): 表示该TCP头部有多少个32位bit(有多少个4字节),所以TCP头部大长度是15 * 4 = 60。根据该部分可以将TCP报头和有效载荷分离。TCP报文默认大小为20个字节。

6位标志位:

URG:它为了标志紧急指针是否有效。 ACK:标识确认号是否有效。 PSH:提示接收端应用程序立即将接收缓冲区的数据拿走。 RST:它是为了处理异常连接的, 告诉连接不一致的一方,我们的连接还没有建立好, 要求对方重新建立连接。我们把携带RST标识的称为复位报文段。 SYN: 请求建立连接; 我们把携带SYN标识的称为同步报文段。 FIN:通知对方, 本端要关闭连接了, 我们称携带FIN标识的为结束报文段。

16位的紧急指针:按序到达是TCP协议保证可靠性的一种机制,但是也存在一些报文想优先被处理,这时就可以设置紧急指针,指向该报文即可,同时将紧急指针有效位置位1。16位窗口大小:如果发送方发送大量数据,接收方接收不过来,会导致大量数据丢失。然后接收方可以发送给发送发消息让发送方发慢一点,这是流量控制。接收方将自己接收缓冲器剩余空间的大小告诉发送方叫做16位窗口大小。发送发可以根据窗口大小来适配发送的速度和大小,窗口大小最大是2的16次方,及64KB,但也可以根据选项中的某些位置扩展,最大扩展1G。16位校验和:发送端填充,CRC校验。如果接收端校验不通过, 则认为数据有问题(此处的检验和不光包含TCP首部也包含TCP数据部分)。

确认应答机制: 在这里插入图片描述

接收端收到一条报文后,向发送端发送一条确认ACK,此ACK的作用就是告诉发送端:接收端已经成功的收到了消息,并且希望收到下一条报文的序列号是什么。这个确认号就是期望的下一个报文的序号。

每一个ACK都带有对应的确认序列号,意思是告诉发送者,我们已经收到了哪些数据,下一个发送数据应该从哪里开始。 如上图,主机A给主机B发送了1-1000的数据,ACK应答,携带了1001序列号。告诉主机A,我已经接受到了1-1000数据,下一次你从1001开始发送数据。

超时重传: 在这里插入图片描述

TCP在传输数据过程中,还加入了超时重传机制。假设主机A发送数据给主机B,主机B没有收到数据包,主机B自然就不会应答,如果主机A在一个特定时间间隔内没有收到主机B发来的确认应答,就会进行重发,这就是超时重传机制。 当然还存在另一种可能就是主机A未收到B发来的确认应答,也可能是因为ACK丢失了。 在这里插入图片描述

因此主机B会收到很多重复数据,那么TCP协议需要能够识别出那些包是重复的包, 并且把重复的包丢弃掉,这时候我们可以利用前面提到的16位序列号, 就可以很容易做到去重的效果。

超时重发的时间应该如何确定? 在理想的情况下,可以找到一个小的时间来保证 "确认应答"一定能在这个时间内返回。但是这个时间的长短,随着网络环境的不同是有差异的。如果超时时间设的太长,会影响整体的重传效率。如果超时时间设的太短,有可能会频繁发送重复的包。TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间。

Linux中超时时间以500ms为一个单位进行控制,每次判定超时重发的超时时间都是500ms的整数倍。如果重发一次之后,仍然得不到应答,等待2*500ms后再进行重传。如果仍然得不到应答,等待4*500ms进行重传。依次类推,以指数形式递增,当累计到一定的重传次数,TCP认为网络或者对端主机出现异常,强制关闭连接。

连接管理机制

在正常情况下, TCP要经过三次握手建立连接,四次挥手断开连接。

三次握手及四次挥手:https://mp.csdn.net/mdeditor/86495932

TIME_WAIT状态: 当我们实现一个TCP服务器时,我们把这个服务器运行起来然后将服务器关闭掉,再次重新启动服务器会发现一个问题:就是不能马上再次绑定这个端口号和ip,需要等一会才可以重新绑定,其实等的这一会就是TIME_WAIT状态。

TCP协议规定主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL的时间后才能回到CLOSED状态。当我们使用Ctrl-C终止了server,server是主动关闭连接的一方在TIME_WAIT期间仍然不能再次监听同样的server端口。MSL在RFC1122中规定为两分钟(120s),但是各操作系统的实现不同,在Centos7上默认配置的值是60s可以通过cat /proc/sys/net/ipv4/tcp_fin_timeout查看MSL的值。

为什么TIME_WAIT时间一定是2MSL:

首先,TIME_WAIT是为了防止最后一个ACK丢失,如果没有TIME_WAIT,那么主动断开连接的一方就已经关闭连接,但是另一方还没有断开连接,它收不到确认ACK会认为自己上次发送的FIN报文丢失会重发该报文,但是另一方已经断开连接了,这就会造成连接不一致的问题,所以TIME_WAIT是必须的。

MSL是TCP报文在发送缓冲区的最大生存时间,如果TIME_WAIT持续存在2MSL的话就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失。(否则服务器立刻重启,可能会收到来自上一个进程的迟到的数据,但是这种数据很可能是错误的)。同时也是在理论上保证最后一个报文可靠到达。(假设最后一个ACK丢失, 那么服务器会再重发一个FIN,这时虽然客户端的进程不在了,但是TCP连接还在,仍然可以重发LAST_ACK,这就会导致问题)

解决TIME_WAIT状态引起的bind失败的方法:

在server的TCP连接没有完全断开之前不允许重新绑定,也就是TIME_WAIT时间没有过,但是这样不允许立即绑定在某些情况下是不合理的:

服务器需要处理非常大量的客户端的连接 (每个连接的生存时间可能很短,但是每秒都有很大数量的客户 端来请求)这个时候如果由服务器端主动关闭连接(比如某些客户端不活跃,就需要被服务器端主动清理掉),这样服务器端就会产生大量TIME_WAIT状态如果客户端的请求量很大,就可能导致TIME_WAIT的连接数很多,每个连接都会占用一个通信五元组(源ip, 源端口, 目的ip, 目的端口, 协议)。其中服务器的ip和端口和协议是固定的,如果新来的客户端连接的ip和端口号和TIME_WAIT占用的连接重复就造成等待。

解决方法:使用setsockopt()设置socket描述符的选项SO_REUSEADDR为1,表示允许创建端口号相同但IP地址不同的多个socket描述符。 关于setsockopt:https://www.cnblogs.com/clschao/articles/9588313.html

服务器端CLOSE_WAIT状态: 如果客户端是主动断开连接的一方,在服务器端假设没有关闭新连接,这时服务器端就会产生一个CLOSE_WAIT状态,因为服务器没有去关闭连接,所以这个CLOSE_WAIT状态很容易测试出来,这时四次挥手没有结束,只完成了两次。

#include "tcp_socket.hpp" typedef void (*Handler)(string& req, string* res); class TcpServer { public: TcpServer(string ip, uint16_t port) :_ip(ip) ,_port(port) {} void Start(Handler handler) { //1.创建socket listen_sock.Socket(); //2.绑定ip和端口号 listen_sock.Bind(_ip, _port); //3.监听 listen_sock.Listen(5); while(1) { TcpSocket new_sock; string ip; uint16_t port; //4.接收连接 listen_sock.Accept(&new_sock, &ip, &port); cout //此处服务器端不关闭新连接,导致CLOSE_WAIT状态 //new_sock.Close(); break; } //6.处理请求 string res; handler(req, &res); //写回处理结果 new_sock.Send(res); cout


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3